package com.bill99.golden.inf.sso.service;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.bill99.golden.inf.sso.mdp.dto.CreateTokenRequestDto;
import com.bill99.golden.inf.sso.mdp.dto.DestroyTokenRequestDto;
import com.bill99.golden.inf.sso.mdp.dto.RefreshTokenTimeRequestDto;
import com.bill99.golden.inf.sso.mdp.dto.ValidateTokenRequestDto;
import com.bill99.golden.inf.sso.mdp.dto.ValidateTokenResponseDto;
import com.bill99.golden.inf.sso.mdp.model.SsoTokenInfo;
import com.bill99.golden.inf.sso.model.AppConfigInfoItem;
import com.bill99.golden.inf.sso.util.CM;
import com.bill99.golden.inf.sso.util.TokenLogUtil;
import com.ryan.utils.MD5Util;

public class TokenService {
	private static Log log = LogFactory.getLog(TokenService.class);
	private static final Map<String, SsoTokenInfo> tokenMap = new ConcurrentHashMap<String, SsoTokenInfo>();
	private static int timeoutMinutes = 15;
	private static long globalIndex = 0;
	private static Object lock = new Object();
	private static String SSO_SERVER_NAME = "sso1";
	private static IAppInfoData appInfoData;
	private static final Map<String, Set<String>> tokenAppCodeMap = new ConcurrentHashMap<String, Set<String>>();

	public static String createToken(CreateTokenRequestDto r) {
		log.debug("注册SSO登录信息：" + r);
		if (r == null) {
			return "none";
		}
		if (appInfoData != null) {
			Map<String, AppConfigInfoItem> m = appInfoData
					.getAppConfigInfoData();
			if (m != null) {
				if (m.get(r.getAppCode()) == null) {
					log.error("createToken:::appCode has not been setted!!!!!!!appCode="
							+ r.getAppCode());
					return "none";
				}
			}
		}
		SsoTokenInfo s = new SsoTokenInfo();
		s.setAppCode(r.getAppCode());
		s.setStatus(SsoTokenInfo.OK);
		s.setUserIp(r.getUserIp());
		s.setExplorerType(r.getExplorerType());
		s.setUserId(r.getUserId());
		s.setUserCode(r.getUserCode());
		s.setUserName(r.getUserName());
		s.setDepartmentId(r.getDepartmentId());
		s.setDepartmentCode(r.getDepartmentCode());
		s.setDepartmentName(r.getDepartmentName());
		s.setOrgId(r.getOrgId());
		s.setOrgCode(r.getOrgCode());
		s.setOrgName(r.getOrgName());
		s.setMemo(r.getMemo());
		String checkCode = TokenService.generateCheckCode(s.getUserIp(),
				s.getExplorerType());
		s.setCheckCode(checkCode);
		String key = TokenService.uuid();
		tokenMap.put(key, s);
		TokenLogUtil.onCreateToken(key, s);
		return key;
	}

	public static void addTokenToBuffer(String token, SsoTokenInfo s) {
		if (token == null || s == null)
			return;
		if (token.length() <= 4)
			return;
		if (tokenMap.get(token) == null) {
			s.setCheckCode(TokenService.generateCheckCode(s.getUserIp(),
					s.getExplorerType()));
			s.setStatus(SsoTokenInfo.OK);
			tokenMap.put(token, s);
		}
	}

	public static ValidateTokenResponseDto validateToken(
			ValidateTokenRequestDto r) {
		log.debug("验证SSO登录信息：" + r);
		ValidateTokenResponseDto response = new ValidateTokenResponseDto();
		if (r == null) {
			response.setValid(false);
			response.setErrorCode(SsoTokenInfo.ERROR_CODE_REQUEST_TOKEN_ERROR);
			return response;
		}
		if (r.getToken() == null || r.getToken().trim().equals("")) {
			response.setValid(false);
			response.setErrorCode(SsoTokenInfo.ERROR_CODE_REQUEST_TOKEN_ERROR);
			return response;
		}
		if (r.getAppCode() == null || r.getAppCode().trim().equals("")) {
			response.setValid(false);
			log.error("appCode cannot be null!!!!!!!!!");
			response.setErrorCode(SsoTokenInfo.ERROR_CODE_REQUEST_TOKEN_ERROR);
			return response;
		}
		if (appInfoData != null) {
			Map<String, AppConfigInfoItem> m = appInfoData
					.getAppConfigInfoData();
			if (m != null) {
				if (m.get(r.getAppCode()) == null) {
					log.error("appCode has not been setted!!!!!!!appCode="
							+ r.getAppCode());
					response.setValid(false);
					log.error("ValidateTokenResponseDto::appCode cannot be null!!!!!!!!!");
					response.setErrorCode(SsoTokenInfo.ERROR_CODE_UNMATCHED_INFO);
					return response;
				}
			}
		}
		String checkCode = TokenService.generateCheckCode(r.getUserIp(),
				r.getExplorerType());
		SsoTokenInfo s = tokenMap.get(r.getToken());
		response.setValid(false);
		if (s == null) {
			// 没有Token
			response.setValid(false);
			response.setErrorCode(SsoTokenInfo.ERROR_CODE_INVALID_TOKEN);
		} else {
			if (checkCode.equals(s.getCheckCode())
					&& SsoTokenInfo.OK.equals(s.getStatus())) {
				long ut = s.getUpdateTime();
				int outTime = (int) (System.currentTimeMillis() - ut);
				if (outTime <= TokenService.timeoutMinutes * 60000) {
					s.refreshUpdateTime();
					response.setValid(true);
					response.setToken(r.getToken());
					response.setUserId(s.getUserId());
					response.setUserCode(s.getUserCode());
					response.setUserName(s.getUserName());
					response.setDepartmentId(s.getDepartmentId());
					response.setDepartmentCode(s.getDepartmentCode());
					response.setDepartmentName(s.getDepartmentName());
					response.setOrgId(s.getOrgId());
					response.setOrgCode(s.getOrgCode());
					response.setOrgName(s.getOrgName());
					response.setMemo(s.getMemo());
					response.setCheckCode(s.getCheckCode());
					response.setErrorCode(SsoTokenInfo.OK);

					s.addVisitCount();
					// 保存访问日志到数据库
					TokenLogUtil.onValidateToken(r.getToken(), s, "ok");

					// 记录访问的AppCode，用于销毁时使用
					Set<String> set = tokenAppCodeMap.get(r.getToken());
					if (set == null) {
						set = new HashSet<String>();
						set.add(r.getAppCode());
						tokenAppCodeMap.put(r.getToken(), set);
					} else {
						if (set.contains(r.getAppCode()) == false) {
							set.add(r.getAppCode());
						}
					}
				} else {
					// 超时
					response.setValid(false);
					response.setErrorCode(SsoTokenInfo.ERROR_CODE_TOKEN_TIMEOUTED);
				}
			} else {
				// token已销毁
				response.setValid(false);
				response.setErrorCode(SsoTokenInfo.ERROR_CODE_UNMATCHED_INFO);
			}
		}
		return response;
	}

	public static void destroyToken(DestroyTokenRequestDto r) {
		if (r == null)
			return;
		if (r.getToken() == null || r.getToken().trim().equals("")) {
			return;
		}
		String checkCode = TokenService.generateCheckCode(r.getUserIp(),
				r.getExplorerType());
		log.debug("销毁SSO登录信息：" + r);
		if (r.getToken() == null) {
			return;
		}
		SsoTokenInfo s = tokenMap.get(r.getToken());
		if (s != null) {
			if (checkCode.equals(s.getCheckCode())) {
				tokenMap.remove(r.getToken());
				TokenLogUtil.onDestroyToken(r.getToken(), s);
				if (appInfoData != null) {
					Map<String, AppConfigInfoItem> appMap = appInfoData
							.getAppConfigInfoData();
					if (appMap != null) {
						Set<String> set = tokenAppCodeMap.get(r.getToken());
						if (set != null && set.size() > 0) {
							String[] appCodes = new String[set.size()];
							set.toArray(appCodes);
							for (int i = 0; i < appCodes.length; i++) {
								AppConfigInfoItem ac = appMap.get(appCodes[i]);
								if (ac != null
										&& CM.stringIsNotEmpty(ac
												.getLogoutUrl())) {
									log.debug("destry remote session........"
											+ ac);
									DestroyRemoteSSOThread
											.destryRemoteSSOBuffer(appCodes[i],
													r, ac.getLogoutUrl());
								}
							}
						}
						if (set != null) {
							set.clear();
							tokenAppCodeMap.remove(r.getToken());
						}
					}
				}
			}
		}
	}

	public static void refreshToken(RefreshTokenTimeRequestDto r) {
		if (r == null)
			return;
		if (r.getToken() == null || r.getToken().trim().equals("")) {
			return;
		}
		String checkCode = TokenService.generateCheckCode(r.getUserIp(),
				r.getExplorerType());
		log.debug("刷新SSO登录信息：" + r);
		if (r.getToken() == null) {
			return;
		}
		SsoTokenInfo s = tokenMap.get(r.getToken());
		if (s != null) {
			if (checkCode.equals(s.getCheckCode())) {
				s.refreshUpdateTime(r.getVisitTimeSpan().longValue());
				s.addVisitCount();
				TokenLogUtil.onRefreshToken(r.getToken(), s);
			}
		}
	}

	private static String uuid() {
		synchronized (TokenService.lock) {
			TokenService.globalIndex = TokenService.globalIndex + 1;
		}
		long time = System.currentTimeMillis();
		long rand = (long) Math.random() * 1000000;
		StringBuffer sb = new StringBuffer();
		sb.append(TokenService.globalIndex).append("!").append(time)
				.append("#").append(rand).append("@").append(SSO_SERVER_NAME);
		String uid = MD5Util.md5Hex(sb.toString()).toUpperCase();
		return uid;
	}

	static String generateCheckCode(String userIp, String explorerType) {
		String str = userIp + "@%" + explorerType;
		String key = MD5Util.md5Hex(str).toUpperCase();
		return key;
	}

	public static int getSessionCount() {
		return tokenMap.size();
	}

	public static IAppInfoData getAppInfoData() {
		return appInfoData;
	}

	public static void setAppInfoData(IAppInfoData appInfoData) {
		TokenService.appInfoData = appInfoData;
	}

	public static void checkSessionTimeOut() {
		log.info("start checkSessionTimeOut.........time="
				+ new java.util.Date());
		Iterator<Map.Entry<String, SsoTokenInfo>> it = tokenMap.entrySet()
				.iterator();
		int cnt = 0;
		while (it.hasNext()) {
			Map.Entry<String, SsoTokenInfo> en = it.next();
			SsoTokenInfo s = en.getValue();
			if (s == null)
				continue;
			long ut = s.getUpdateTime();
			int outTime = (int) (System.currentTimeMillis() - ut);
			String token = en.getKey();
			if (outTime > TokenService.timeoutMinutes * 60000) {
				// session超时
				tokenMap.remove(token);
				TokenLogUtil.onDestroyToken(token, s);
				cnt++;
				if (appInfoData != null) {
					Map<String, AppConfigInfoItem> appMap = appInfoData
							.getAppConfigInfoData();
					if (appMap != null) {
						Set<String> set = tokenAppCodeMap.get(token);
						if (set != null && set.size() > 0) {
							String[] appCodes = new String[set.size()];
							set.toArray(appCodes);
							for (int i = 0; i < appCodes.length; i++) {
								DestroyTokenRequestDto r = new DestroyTokenRequestDto();
								r.setAppCode(appCodes[i]);
								r.setExplorerType(s.getExplorerType());
								r.setToken(token);
								r.setUserIp(s.getUserIp());
								AppConfigInfoItem ac = appMap.get(appCodes[i]);
								if (ac != null) {
									DestroyRemoteSSOThread
											.destryRemoteSSOBuffer(appCodes[i],
													r, ac.getLogoutUrl());
								}
							}
						}
						if (set != null) {
							set.clear();
							tokenAppCodeMap.remove(token);
						}
					}
				}
			}
		}
		log.info("end checkSessionTimeOut.........timeoutSessions=" + cnt
				+ "  time=" + new java.util.Date());
	}

	public static SsoTokenInfo[] getSsoTokenList() {
		if (tokenMap.size() < 1)
			return null;
		SsoTokenInfo[] st = new SsoTokenInfo[tokenMap.size()];
		return tokenMap.values().toArray(st);
	}

}
